/*
    kOTF Open Trace Format - a library for generating execution traces.
    Copyright 2012 Daniel K. O.

    kOTF is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    kOTF is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with kOTF.  If not, see <http://www.gnu.org/licenses/>.
*/

#ifndef KOTF_STREAM_HELPERS_H
#define KOTF_STREAM_HELPERS_H

#include <kotf/Types.hpp>
#include <kotf/Stream.hpp>

namespace kotf {
    
    /** \brief RAII for state push/pop

        This implements RAII for scoped state management. Create a variable of this type at
        the beginning of a function, and the state supplyed in the argument will be pushed.
        At the destructor (meaning the function exited) the same state will be poped.
        Works of course with any scoped block, not just for functions.
     */
    class Scope {
        Scope(const Scope&);
        Scope& operator=(const Scope&);
        Stream& stream;
        State state;
    public:
        size_t finishTag;

        Scope(const string& stname, size_t startTag=0); ///< convenience, uses Stream::mine()
        Scope(const State& st, size_t startTag=0); ///< convenience, uses Stream::mine()
        Scope(Stream& s, const string& stname, size_t startTag=0);
        Scope(Stream& s, const State& st, size_t startTag=0);
        Scope(Stream* s, const string& stname, size_t startTag=0); // accept pointers too
        Scope(Stream* s, const State& st, size_t startTag=0); // accept pointers too

        ~Scope();

        void change(const State& newst); ///< changes to a new state
    };


    
    /** \brief RAII for scoped mutex locks.

        Many threading libraries provide RAII locking primitives for mutexes. They lock
        a mutex in the constructor and unlock at the destructor. This class performs the same job,
        registering the value 1 in a counter at the constructor, and the value 2 when the
        locked() method is called. The destructor then sets the value back to zero. So
        by using it like this

        \code
        {
            kotf::ScopedLock kguard(s, c);
            boost::mutex::scoped_lock guard(lock);
            kguard.locked();
            ...
        }
        \endcode
        we can interpret the values in the counter c as:
          - 0: mutex is unlocked
          - 1: mutex is being locked by this thread
          - 2: thread finally locked the mutex and is able to proceed
     */
    class ScopedLock {
        ScopedLock(const ScopedLock&);
        ScopedLock& operator=(const ScopedLock&);
        Stream& stream;
        const Counter counter;
    public:
        ScopedLock(Stream& s, const Counter& cnt); ///< sets cnt = 1
        ScopedLock(Stream& s, const string& cntn); ///< sets cnt = 1
        ScopedLock(Stream* s, const Counter& cnt); // accepts pointers too
        ScopedLock(Stream* s, const string& cntn); // accepts pointers too
        ScopedLock(const Counter& cnt); ///< sets cnt = 1, uses Stream::mine()
        ScopedLock(const string& cntn); ///< sets cnt = 1, uses Stream::mine()
        void locked(); ///< sets cnt = 2
        ~ScopedLock(); ///< sets cnt = 0
    };



    /** \brief Helper class for condition variables

        This class does not perform RAII, mostly because waiting for conditions is more complicated.
        Typical usage for condition variables is like this:

        \code
        boost::condition_variable someCond;
        boost::mutex mtx;

        void foo()
        {
            boost::mutex::scoped_lock guard(mtx);

            while (condition not satisfied)
                someCond.wait(guard);
            
            // condition is satisfied here
        }
        \endcode

        So we use kotf::Condition in the above code like this:

        \code
        void foo()
        {
            boost::mutex::scoped_lock guard(mtx);

            Condition ksomeCond(stream, wait_state, notify_channel);
            while (condition not satisfied) {
                ksomeCond.wait();
                someCond.wait(guard);
            
            // condition is satisfied here
            ksomeCond.done();
        }
        \endcode

        With this we will have a trace saying if the thread started sleeping (by calling the
        wait() method), and when it woke up. Even if wait() is called multiple times, only a single
        push() is done for the state.

        The done() method calls pop() for the state, and register a recv() from the channel. Remember
        the thread only returns from condition's wait state if some other thread notified (i.e.
        sent a message) it to wake up. So we register this in the form of message passing. The
        implicit recv() call can be parametrized in the Condition constructor.
     */
    class Condition {
        Condition(const Condition&);
        Condition& operator=(const Condition&);
        Stream& stream;
        State state;
        Channel chan;
        uint64_t length, tag;
        bool waiting;
    public:
        /** constructs a condition variable helper
            
            \arg st the state that will be entered while the thread waits
            \arg ch the channel where the recv() will be called
            \arg length opetional message length for the recv()
            \arg tag optional tag for the recv()
         */
        Condition(Stream& s, const State& st, const Channel& ch, uint64_t length=0, uint64_t tag=0);
        
        void wait(); ///< enters the wait state on the first time.

        void done(); ///< leaves the wait state and receives a message
    };
}

#endif
